/*/*
    This file is part of Kompex SQLite Wrapper.
	Copyright (c) 2008-2024 Sven Broeske

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

#include <iostream>
#include <iomanip>
#include <exception>
#include <sstream>
#include <string.h>

#include "KompexSQLiteStatement.h"
#include "KompexSQLiteDatabase.h"
#include "KompexSQLiteException.h"

namespace Kompex
{

SQLiteStatement::SQLiteStatement(SQLiteDatabase *db):
	mDatabase(db),
	mStatement(0),
	mTransactionID(0),
	mIsColumnNumberAssignedToColumnName(false)
{
}

SQLiteStatement::~SQLiteStatement()
{
	FreeQuery();
	CleanUpTransaction();
}

void SQLiteStatement::Prepare(const char *sqlStatement)
{
	mIsColumnNumberAssignedToColumnName = false;
	CheckDatabase();

	// If the nByte argument is less than zero, 
	// then zSql is read up to the first zero terminator. 

	if(sqlite3_prepare_v2(mDatabase->GetDatabaseHandle(), sqlStatement, -1, &mStatement, 0) != SQLITE_OK)
		KOMPEX_EXCEPT(sqlite3_errmsg(mDatabase->GetDatabaseHandle()), sqlite3_errcode(mDatabase->GetDatabaseHandle()));

	if(!mStatement)
		KOMPEX_EXCEPT("Prepare() SQL statement failed", -1);
}

void SQLiteStatement::Prepare(const wchar_t *sqlStatement)
{
	mIsColumnNumberAssignedToColumnName = false;
	CheckDatabase();

	// If the nByte argument is less than zero, 
	// then zSql is read up to the first zero terminator. 

	if(sqlite3_prepare16_v2(mDatabase->GetDatabaseHandle(), sqlStatement, -1, &mStatement, 0) != SQLITE_OK)
		KOMPEX_EXCEPT(sqlite3_errmsg(mDatabase->GetDatabaseHandle()), sqlite3_errcode(mDatabase->GetDatabaseHandle()));

	if(!mStatement)
		KOMPEX_EXCEPT("Prepare() SQL statement failed", -1);
}

bool SQLiteStatement::Step() const
{
	switch(sqlite3_step(mStatement))
	{
		// sqlite3_step() has finished executing
		case SQLITE_DONE:
			return false;
		// sqlite3_step() has another row ready
		case SQLITE_ROW:
			return true;
		default:
			KOMPEX_EXCEPT(sqlite3_errmsg(mDatabase->GetDatabaseHandle()), sqlite3_errcode(mDatabase->GetDatabaseHandle()));
	}
}

void SQLiteStatement::SqlStatement(const char *sqlStatement)
{
	Prepare(sqlStatement);
	Step();
	FreeQuery();
}

void SQLiteStatement::SqlStatement(const wchar_t *sqlStatement)
{
	Prepare(sqlStatement);
	Step();
	FreeQuery();
}

bool SQLiteStatement::FetchRow() const
{
	int rc = sqlite3_step(mStatement);

	switch(rc)
	{
		case SQLITE_BUSY:
			KOMPEX_EXCEPT("FetchRow() SQLITE_BUSY", SQLITE_BUSY);
			return false;
		case SQLITE_DONE:
			return false;
		case SQLITE_ROW:
			return true;
		case SQLITE_ERROR:
			KOMPEX_EXCEPT(sqlite3_errmsg(mDatabase->GetDatabaseHandle()), SQLITE_ERROR);
			return false;
		case SQLITE_MISUSE:
			KOMPEX_EXCEPT("FetchRow() SQLITE_MISUSE", SQLITE_MISUSE);
			return false;
	}

	return false;
}

void SQLiteStatement::FreeQuery()
{
	// destroy prepared statement
	sqlite3_finalize(mStatement);
	mStatement = 0;
}

void SQLiteStatement::CheckStatement() const
{
	if(!mStatement)
		KOMPEX_EXCEPT("empty statement pointer", -1);
}

void SQLiteStatement::CheckDatabase() const
{
	if(!mDatabase)
		KOMPEX_EXCEPT("database pointer invalid", -1);
}

double SQLiteStatement::SqlAggregateFuncResult(const std::string &countSql)
{
	double result = 0;

	Sql(countSql);
	while(FetchRow())
		result = GetColumnDouble(0);
	
	FreeQuery();
	return result;
}

double SQLiteStatement::SqlAggregateFuncResult(wchar_t *countSql)
{
	double result = 0;

	Sql(countSql);
	while(FetchRow())
		result = GetColumnDouble(0);
	
	FreeQuery();
	return result;
}

double SQLiteStatement::SqlAggregateFuncResult(const char *countSql)
{
	double result = 0;

	Sql(countSql);
	while(FetchRow())
		result = GetColumnDouble(0);
	
	FreeQuery();
	return result;
}

//------------------------------------------------------------------------------------
// GetColumn...() Methods

const char *SQLiteStatement::GetColumnName(int column) const
{
	CheckStatement();
	CheckColumnNumber(column, "GetColumnName()");

	return sqlite3_column_name(mStatement, column);
}

wchar_t *SQLiteStatement::GetColumnName16(int column) const
{
	CheckStatement();
	CheckColumnNumber(column, "GetColumnName16()");

	return (wchar_t*)sqlite3_column_name16(mStatement, column);
}

const unsigned char *SQLiteStatement::GetColumnCString(int column) const
{
	CheckStatement();
	CheckColumnNumber(column, "GetColumnCString()");

	return sqlite3_column_text(mStatement, column);
}

std::string SQLiteStatement::GetColumnString(int column) const
{
	CheckStatement();
	CheckColumnNumber(column, "GetColumnString()");

	const unsigned char *result = sqlite3_column_text(mStatement, column);

	// capture NULL results
	if(result == 0)
		return "";

	std::stringstream ss;
	ss << result;
	return ss.str();
}

double SQLiteStatement::GetColumnDouble(int column) const
{
	CheckStatement();
	CheckColumnNumber(column, "GetColumnDouble()");

	return sqlite3_column_double(mStatement, column);
}

int SQLiteStatement::GetColumnInt(int column) const
{
	CheckStatement();
	CheckColumnNumber(column, "GetColumnInt()");

	return sqlite3_column_int(mStatement, column);
}

bool SQLiteStatement::GetColumnBool(int column) const
{
	CheckStatement();
	CheckColumnNumber(column, "GetColumnBool()");

	return !!sqlite3_column_int(mStatement, column);
}

int64 SQLiteStatement::GetColumnInt64(int column) const
{
	CheckStatement();
	CheckColumnNumber(column, "GetColumnInt64()");

	return sqlite3_column_int64(mStatement, column);
}

int SQLiteStatement::GetColumnType(int column) const
{
	CheckStatement();
	CheckColumnNumber(column, "GetColumnType()");

	return sqlite3_column_type(mStatement, column);
}

wchar_t *SQLiteStatement::GetColumnString16(int column) const
{
	CheckStatement();
	CheckColumnNumber(column, "GetColumnString16()");

	return (wchar_t*)sqlite3_column_text16(mStatement, column);
}

const void *SQLiteStatement::GetColumnBlob(int column) const
{
	CheckStatement();
	CheckColumnNumber(column, "GetColumnBlob()");

	return sqlite3_column_blob(mStatement, column);
}

int SQLiteStatement::GetColumnCount() const
{
	CheckStatement();
	return sqlite3_column_count(mStatement);
}

int SQLiteStatement::GetColumnBytes(int column) const
{
	CheckStatement();
	CheckColumnNumber(column, "GetColumnBytes()");

	return sqlite3_column_bytes(mStatement, column);
}

int SQLiteStatement::GetColumnBytes16(int column) const
{
	CheckStatement();
	CheckColumnNumber(column, "GetColumnBytes16()");

	return sqlite3_column_bytes16(mStatement, column);
}

const char *SQLiteStatement::GetColumnDatabaseName(int column) const
{
	CheckStatement();
	CheckColumnNumber(column, "GetColumnDatabaseName()");

	return sqlite3_column_database_name(mStatement, column);
}

wchar_t *SQLiteStatement::GetColumnDatabaseName16(int column) const
{
	CheckStatement();
	CheckColumnNumber(column, "GetColumnDatabaseName16()");

	return (wchar_t*)sqlite3_column_database_name16(mStatement, column);
}

const char *SQLiteStatement::GetColumnTableName(int column) const
{
	CheckStatement();
	CheckColumnNumber(column, "GetColumnTableName()");

	return sqlite3_column_table_name(mStatement, column);
}

wchar_t *SQLiteStatement::GetColumnTableName16(int column) const
{
	CheckStatement();
	CheckColumnNumber(column, "GetColumnTableName16()");

	return (wchar_t*)sqlite3_column_table_name16(mStatement, column);
}

const char *SQLiteStatement::GetColumnOriginName(int column) const
{
	CheckStatement();
	CheckColumnNumber(column, "GetColumnOriginName()");

	return sqlite3_column_origin_name(mStatement, column);
}

wchar_t *SQLiteStatement::GetColumnOriginName16(int column) const
{
	CheckStatement();
	CheckColumnNumber(column, "GetColumnOriginName16()");

	return (wchar_t*)sqlite3_column_origin_name16(mStatement, column);
}

const char *SQLiteStatement::GetColumnDeclaredDatatype(int column) const
{
	CheckStatement();
	CheckColumnNumber(column, "GetColumnDeclaredDatatype()");

	return sqlite3_column_decltype(mStatement, column);
}

wchar_t *SQLiteStatement::GetColumnDeclaredDatatype16(int column) const
{
	CheckStatement();
	CheckColumnNumber(column, "GetColumnDeclaredDatatype16()");

	return (wchar_t*)sqlite3_column_decltype16(mStatement, column);
}

int SQLiteStatement::GetColumnBytes(const std::string &column) const
{
	AssignColumnNumberToColumnName();
	return sqlite3_column_bytes(mStatement, GetAssignedColumnNumber(column));
}

int SQLiteStatement::GetColumnBytes16(const std::string &column) const
{
	AssignColumnNumberToColumnName();
	return sqlite3_column_bytes16(mStatement, GetAssignedColumnNumber(column));
}

const char *SQLiteStatement::GetColumnDatabaseName(const std::string &column) const
{
	AssignColumnNumberToColumnName();
	return sqlite3_column_database_name(mStatement, GetAssignedColumnNumber(column));
}

wchar_t *SQLiteStatement::GetColumnDatabaseName16(const std::string &column) const
{
	AssignColumnNumberToColumnName();
	return (wchar_t*)sqlite3_column_database_name16(mStatement, GetAssignedColumnNumber(column));
}

const char *SQLiteStatement::GetColumnTableName(const std::string &column) const
{
	AssignColumnNumberToColumnName();
	return sqlite3_column_table_name(mStatement, GetAssignedColumnNumber(column));
}

wchar_t *SQLiteStatement::GetColumnTableName16(const std::string &column) const
{
	AssignColumnNumberToColumnName();
	return (wchar_t*)sqlite3_column_table_name16(mStatement, GetAssignedColumnNumber(column));
}

const char *SQLiteStatement::GetColumnOriginName(const std::string &column) const
{
	AssignColumnNumberToColumnName();
	return sqlite3_column_origin_name(mStatement, GetAssignedColumnNumber(column));
}

wchar_t *SQLiteStatement::GetColumnOriginName16(const std::string &column) const
{
	AssignColumnNumberToColumnName();
	return (wchar_t*)sqlite3_column_origin_name16(mStatement, GetAssignedColumnNumber(column));
}

const char *SQLiteStatement::GetColumnDeclaredDatatype(const std::string &column) const
{
	AssignColumnNumberToColumnName();
	return sqlite3_column_decltype(mStatement, GetAssignedColumnNumber(column));
}

wchar_t *SQLiteStatement::GetColumnDeclaredDatatype16(const std::string &column) const
{
	AssignColumnNumberToColumnName();
	return (wchar_t*)sqlite3_column_decltype16(mStatement, GetAssignedColumnNumber(column));
}

const char *SQLiteStatement::GetColumnName(const std::string &column) const
{
	AssignColumnNumberToColumnName();
	return sqlite3_column_name(mStatement, GetAssignedColumnNumber(column));
}

wchar_t *SQLiteStatement::GetColumnName16(const std::string &column) const
{
	AssignColumnNumberToColumnName();
	return (wchar_t*)sqlite3_column_name16(mStatement, GetAssignedColumnNumber(column));
}

const unsigned char *SQLiteStatement::GetColumnCString(const std::string &column) const
{
	AssignColumnNumberToColumnName();
	return sqlite3_column_text(mStatement, GetAssignedColumnNumber(column));
}

std::string SQLiteStatement::GetColumnString(const std::string &column) const
{
	AssignColumnNumberToColumnName();

	const unsigned char *result = sqlite3_column_text(mStatement, GetAssignedColumnNumber(column));

	// capture NULL results
	if(result == 0)
		return "";

	std::stringstream ss;
	ss << result;
	return ss.str();
}

double SQLiteStatement::GetColumnDouble(const std::string &column) const
{
	AssignColumnNumberToColumnName();
	return sqlite3_column_double(mStatement, GetAssignedColumnNumber(column));
}

int SQLiteStatement::GetColumnInt(const std::string &column) const
{
	AssignColumnNumberToColumnName();
	return sqlite3_column_int(mStatement, GetAssignedColumnNumber(column));
}

bool SQLiteStatement::GetColumnBool(const std::string &column) const
{
	AssignColumnNumberToColumnName();
	return !!sqlite3_column_int(mStatement, GetAssignedColumnNumber(column));
}

int64 SQLiteStatement::GetColumnInt64(const std::string &column) const
{
	AssignColumnNumberToColumnName();
	return sqlite3_column_int64(mStatement, GetAssignedColumnNumber(column));
}

int SQLiteStatement::GetColumnType(const std::string &column) const
{
	AssignColumnNumberToColumnName();
	return sqlite3_column_type(mStatement, GetAssignedColumnNumber(column));
}

wchar_t *SQLiteStatement::GetColumnString16(const std::string &column) const
{
	AssignColumnNumberToColumnName();
	return (wchar_t*)sqlite3_column_text16(mStatement, GetAssignedColumnNumber(column));
}

const void *SQLiteStatement::GetColumnBlob(const std::string &column) const
{
	AssignColumnNumberToColumnName();
	return sqlite3_column_blob(mStatement, GetAssignedColumnNumber(column));
}

//------------------------------------------------------------------------------------
// Bind...() Methods

void SQLiteStatement::BindInt(int column, int value) const
{
	if(sqlite3_bind_int(mStatement, column, value) != SQLITE_OK)
		KOMPEX_EXCEPT(sqlite3_errmsg(mDatabase->GetDatabaseHandle()), sqlite3_errcode(mDatabase->GetDatabaseHandle()));
}

void SQLiteStatement::BindBool(int column, bool value) const
{
	if(sqlite3_bind_int(mStatement, column, static_cast<int>(value)) != SQLITE_OK)
		KOMPEX_EXCEPT(sqlite3_errmsg(mDatabase->GetDatabaseHandle()), sqlite3_errcode(mDatabase->GetDatabaseHandle()));
}

void SQLiteStatement::BindString(int column, const std::string &string) const
{
	// SQLITE_TRANSIENT: SQLite makes its own private copy of the data immediately, before the sqlite3_bind_*() routine returns
	if(sqlite3_bind_text(mStatement, column, string.c_str(), string.length(), SQLITE_TRANSIENT) != SQLITE_OK)
		KOMPEX_EXCEPT(sqlite3_errmsg(mDatabase->GetDatabaseHandle()), sqlite3_errcode(mDatabase->GetDatabaseHandle()));
}

void SQLiteStatement::BindString16(int column, const wchar_t *string) const
{
	// SQLITE_TRANSIENT: SQLite makes its own private copy of the data immediately, before the sqlite3_bind_*() routine returns
	if(sqlite3_bind_text16(mStatement, column, string, -1, SQLITE_TRANSIENT) != SQLITE_OK)
		KOMPEX_EXCEPT(sqlite3_errmsg(mDatabase->GetDatabaseHandle()), sqlite3_errcode(mDatabase->GetDatabaseHandle()));
}

void SQLiteStatement::BindString64(int column, const char *string, uint64 byteLength, ENCODING encoding) const
{
	// SQLITE_TRANSIENT: SQLite makes its own private copy of the data immediately, before the sqlite3_bind_*() routine returns
	switch(sqlite3_bind_text64(mStatement, column, string, byteLength, SQLITE_TRANSIENT, encoding))
	{
		case SQLITE_OK:
			break;
		case SQLITE_TOOBIG:
			KOMPEX_EXCEPT("SQLITE_TOOBIG - string was larger than stated in byteLength or exceeded the default maximum length", -1);
		default:
			KOMPEX_EXCEPT(sqlite3_errmsg(mDatabase->GetDatabaseHandle()), sqlite3_errcode(mDatabase->GetDatabaseHandle()));
	}
}

void SQLiteStatement::BindDouble(int column, double value) const
{
	if(sqlite3_bind_double(mStatement, column, value) != SQLITE_OK)
		KOMPEX_EXCEPT(sqlite3_errmsg(mDatabase->GetDatabaseHandle()), sqlite3_errcode(mDatabase->GetDatabaseHandle()));
}

void SQLiteStatement::BindInt64(int column, int64 value) const
{
	if(sqlite3_bind_int64(mStatement, column, value) != SQLITE_OK)
		KOMPEX_EXCEPT(sqlite3_errmsg(mDatabase->GetDatabaseHandle()), sqlite3_errcode(mDatabase->GetDatabaseHandle()));
}

void SQLiteStatement::BindNull(int column) const
{
	if(sqlite3_bind_null(mStatement, column) != SQLITE_OK)
		KOMPEX_EXCEPT(sqlite3_errmsg(mDatabase->GetDatabaseHandle()), sqlite3_errcode(mDatabase->GetDatabaseHandle()));
}

void SQLiteStatement::BindBlob(int column, const void* data, int numberOfBytes) const
{
	if(sqlite3_bind_blob(mStatement, column, data, numberOfBytes, SQLITE_TRANSIENT) != SQLITE_OK)
		KOMPEX_EXCEPT(sqlite3_errmsg(mDatabase->GetDatabaseHandle()), sqlite3_errcode(mDatabase->GetDatabaseHandle()));
}

void SQLiteStatement::BindBlob64(int column, const void* data, uint64 numberOfBytes) const
{
	if(sqlite3_bind_blob64(mStatement, column, data, numberOfBytes, SQLITE_TRANSIENT) != SQLITE_OK)
		KOMPEX_EXCEPT(sqlite3_errmsg(mDatabase->GetDatabaseHandle()), sqlite3_errcode(mDatabase->GetDatabaseHandle()));
}

void SQLiteStatement::BindZeroBlob(int column, int length) const
{
	if(sqlite3_bind_zeroblob(mStatement, column, length) != SQLITE_OK)
		KOMPEX_EXCEPT(sqlite3_errmsg(mDatabase->GetDatabaseHandle()), sqlite3_errcode(mDatabase->GetDatabaseHandle()));
}

void SQLiteStatement::BindZeroBlob64(int column, uint64 length) const
{
	if(sqlite3_bind_zeroblob64(mStatement, column, length) != SQLITE_OK)
		KOMPEX_EXCEPT(sqlite3_errmsg(mDatabase->GetDatabaseHandle()), sqlite3_errcode(mDatabase->GetDatabaseHandle()));
}

//------------------------------------------------------------------------------------
void SQLiteStatement::ExecuteAndFree()
{
	Step();
	FreeQuery();
}

void SQLiteStatement::Execute() const
{
	Step();
}

void SQLiteStatement::GetTable(const std::string &sql, unsigned short consoleOutputColumnWidth) const
{
	CheckDatabase();

	char *errMsg;
	char **result;
	int rows, columns;
	
	if(sqlite3_get_table(mDatabase->GetDatabaseHandle(), sql.c_str(), &result, &rows, &columns, &errMsg) != SQLITE_OK)
		KOMPEX_EXCEPT(sqlite3_errmsg(mDatabase->GetDatabaseHandle()), sqlite3_errcode(mDatabase->GetDatabaseHandle()));

	int counter = 0;
	for(int r = 0; r <= rows; ++r)
	{
		for(int c = 0; c < columns; ++c)
		{
			std::cout << std::left << std::setw(consoleOutputColumnWidth - 3);
			if(result[counter])
				std::cout << result[counter];
			else
				std::cout << "NULL";

			if(c < columns - 1)
				std::cout << " | ";

			counter++;
		}
		std::cout << std::endl;

		if(r == 0)
		{
			for(int dum = consoleOutputColumnWidth * columns; dum != 0; --dum)
				std::cout << "-";

			std::cout << std::endl;
		}
	}

	sqlite3_free_table(result);
}

void SQLiteStatement::GetTableColumnMetadata(const std::string &tableName, const std::string &columnName) const
{
	CheckDatabase();

	int notnull, primaryKey, autoInc;
	const char *dataType, *collSeq;

	if(sqlite3_table_column_metadata(mDatabase->GetDatabaseHandle(), 0, tableName.c_str(), columnName.c_str(), &dataType, &collSeq, &notnull, &primaryKey, &autoInc))
		KOMPEX_EXCEPT(sqlite3_errmsg(mDatabase->GetDatabaseHandle()), sqlite3_errcode(mDatabase->GetDatabaseHandle()));
			
	std::cout << "TableColumnMetadata:" << std::endl;
	std::cout << "data type: " << dataType << std::endl;
	std::cout << "collation sequence: " << primaryKey << std::endl;
	std::cout << "not null: " << notnull << std::endl;
	std::cout << "primary key: " << primaryKey << std::endl;
	std::cout << "auto increment: " << autoInc << std::endl;
}

void SQLiteStatement::ClearBindings() const
{
	CheckStatement();

	if(sqlite3_clear_bindings(mStatement) != SQLITE_OK)
		KOMPEX_EXCEPT(sqlite3_errmsg(mDatabase->GetDatabaseHandle()), sqlite3_errcode(mDatabase->GetDatabaseHandle()));
}

void SQLiteStatement::Reset() const
{
	CheckStatement();

	if(sqlite3_reset(mStatement) != SQLITE_OK)
		KOMPEX_EXCEPT(sqlite3_errmsg(mDatabase->GetDatabaseHandle()), sqlite3_errcode(mDatabase->GetDatabaseHandle()));
}

void SQLiteStatement::CommitTransaction() 
{
	if(!mTransactionSQL.empty() || !mTransactionSQL16.empty())
	{
		try
		{
			int i = 0;
			// check wheter we have sql statements with different data types
			if(!mTransactionSQL.empty() && !mTransactionSQL16.empty())
			{
				TTransactionSQL::iterator transIter;
				TTransactionSQL16::iterator trans16Iter;

				unsigned short transactions = mTransactionSQL.size() + mTransactionSQL16.size();

				while(i < transactions)
				{
					transIter = mTransactionSQL.find(i);
					if(transIter != mTransactionSQL.end())
					{
						SqlStatement(transIter->second.first);
					}
					else
					{
						trans16Iter = mTransactionSQL16.find(i);
						if(trans16Iter != mTransactionSQL16.end())
						{
							SqlStatement(trans16Iter->second.first);
						}					
						else
						{
							KOMPEX_EXCEPT("CommitTransaction() transaction id not found", -1);
						}
					}

					++i;
				}
			}
			else
			{
				// because we have only one data type, we can execute all sql statements from the filled container
				if(!mTransactionSQL.empty())
				{
					for(TTransactionSQL::iterator transIter = mTransactionSQL.begin(); transIter != mTransactionSQL.end(); ++transIter)
						SqlStatement(transIter->second.first);
				}
				else
				{
					for(TTransactionSQL16::iterator trans16Iter = mTransactionSQL16.begin(); trans16Iter != mTransactionSQL16.end(); ++trans16Iter)
						SqlStatement(trans16Iter->second.first);
				}
			}

			SqlStatement("COMMIT;");
			CleanUpTransaction();
		}
		catch(SQLiteException &exception)
		{
			std::cerr << "Exception Occured!" << std::endl;
			exception.Show();
			RollbackTransaction();
			std::cerr << "Rollback has been executed!" << std::endl;
			CleanUpTransaction();
		}
	}
	else
	{
		SqlStatement("COMMIT;");
	}
	mTransactionID = 0;
}

void SQLiteStatement::CleanUpTransaction()
{
	// clean up memory and container
	for(TTransactionSQL::iterator transIter = mTransactionSQL.begin(); transIter != mTransactionSQL.end(); ++transIter)
		DeleteTransactionSqlStr(transIter->second.second, transIter->second.first);
	mTransactionSQL.clear();

	for(TTransactionSQL16::iterator trans16Iter = mTransactionSQL16.begin(); trans16Iter != mTransactionSQL16.end(); ++trans16Iter)
		DeleteTransactionSqlStr(trans16Iter->second.second, trans16Iter->second.first);
	mTransactionSQL16.clear();
}

void SQLiteStatement::BeginTransaction() 
{
	SqlStatement("BEGIN;");
	CleanUpTransaction();
}

void SQLiteStatement::SecureTransaction(const char *sql)
{
	char *buffer = new char[strlen(sql) + 1];
	strcpy(buffer, sql);
	mTransactionSQL[mTransactionID++] = std::make_pair(buffer, true);
}

void SQLiteStatement::SecureTransaction(const std::string sql)
{
	char *buffer = new char[sql.length() + 1];
	strcpy(buffer, sql.c_str());
	mTransactionSQL[mTransactionID++] = std::make_pair(buffer, true);
}

void SQLiteStatement::SecureTransaction(const wchar_t *sql) 
{
	wchar_t *buffer = new wchar_t[wcslen(sql) + 1];
	wcscpy(buffer, sql);
	mTransactionSQL16[mTransactionID++] = std::make_pair(buffer, true);
}

//------------------------------------------------------------------------------------
template<class S, class T>
T SQLiteStatement::GetColumnValue(S sql, T(Kompex::SQLiteStatement::*getColumnFunc)(int columnNumber)const, T defaultReturnValue)
{
	Sql(sql);

	T queryResult;

	if(!FetchRow())
		queryResult = defaultReturnValue;
	else
		queryResult = (this->*getColumnFunc)(0);
	
	FreeQuery();

	return queryResult;
}

std::string SQLiteStatement::GetSqlResultString(const std::string &sql, const std::string &defaultReturnValue) 
{
	return GetColumnValue(sql, &Kompex::SQLiteStatement::GetColumnString, defaultReturnValue);
}

std::string SQLiteStatement::GetSqlResultString(const char *sql, const std::string &defaultReturnValue) 
{
	return GetColumnValue(sql, &Kompex::SQLiteStatement::GetColumnString, defaultReturnValue);
}

std::string SQLiteStatement::GetSqlResultString(const wchar_t *sql, const std::string &defaultReturnValue)
{
	return GetColumnValue(sql, &Kompex::SQLiteStatement::GetColumnString, defaultReturnValue);
}

int SQLiteStatement::GetSqlResultInt(const std::string &sql, int defaultReturnValue) 
{
	return GetColumnValue(sql, &Kompex::SQLiteStatement::GetColumnInt, defaultReturnValue);
}

int SQLiteStatement::GetSqlResultInt(const char *sql, int defaultReturnValue) 
{
	return GetColumnValue(sql, &Kompex::SQLiteStatement::GetColumnInt, defaultReturnValue);
}

int SQLiteStatement::GetSqlResultInt(const wchar_t *sql, int defaultReturnValue) 
{
	return GetColumnValue(sql, &Kompex::SQLiteStatement::GetColumnInt, defaultReturnValue);
}

int64 SQLiteStatement::GetSqlResultInt64(const std::string &sql, int64 defaultReturnValue) 
{
	return GetColumnValue<std::string, int64>(sql, &Kompex::SQLiteStatement::GetColumnInt64, defaultReturnValue);
}

int64 SQLiteStatement::GetSqlResultInt64(const char *sql, int64 defaultReturnValue) 
{
	return GetColumnValue<const char*, int64>(sql, &Kompex::SQLiteStatement::GetColumnInt64, defaultReturnValue);
}

int64 SQLiteStatement::GetSqlResultInt64(const wchar_t *sql, int64 defaultReturnValue) 
{
	return GetColumnValue<const wchar_t*, int64>(sql, &Kompex::SQLiteStatement::GetColumnInt64, defaultReturnValue);
}

double SQLiteStatement::GetSqlResultDouble(const std::string &sql, double defaultReturnValue) 
{
	return GetColumnValue(sql, &Kompex::SQLiteStatement::GetColumnDouble, defaultReturnValue);
}

double SQLiteStatement::GetSqlResultDouble(const char *sql, double defaultReturnValue) 
{
	return GetColumnValue(sql, &Kompex::SQLiteStatement::GetColumnDouble, defaultReturnValue);
}

double SQLiteStatement::GetSqlResultDouble(const wchar_t *sql, double defaultReturnValue) 
{
	return GetColumnValue(sql, &Kompex::SQLiteStatement::GetColumnDouble, defaultReturnValue);
}

const unsigned char *SQLiteStatement::SqlResultCString(const unsigned char *defaultReturnValue)
{
	const unsigned char *queryResult;

	if(!FetchRow())
		queryResult = defaultReturnValue;
	else
		queryResult = SQLiteStatement::GetColumnCString(0);
	
	std::stringstream strStream;
	strStream << queryResult;
	std::string result = strStream.str();

	unsigned char *buffer = new unsigned char[result.length() + 1];
	memcpy(buffer, result.c_str(), result.length() + 1);

	FreeQuery();

	return buffer;
}

const unsigned char *SQLiteStatement::GetSqlResultCString(const std::string &sql, const unsigned char *defaultReturnValue) 
{
	Sql(sql);
	return SqlResultCString(defaultReturnValue);
}

const unsigned char *SQLiteStatement::GetSqlResultCString(const char *sql, const unsigned char *defaultReturnValue) 
{
	Sql(sql);
	return SqlResultCString(defaultReturnValue);
}

const unsigned char *SQLiteStatement::GetSqlResultCString(const wchar_t *sql, const unsigned char *defaultReturnValue) 
{
	Sql(sql);
	return SqlResultCString(defaultReturnValue);
}

wchar_t *SQLiteStatement::SqlResultString16(wchar_t *defaultReturnValue)
{
	wchar_t *queryResult;

	if(!FetchRow())
		queryResult = defaultReturnValue;
	else
		queryResult = SQLiteStatement::GetColumnString16(0);
	
	std::wstringstream wstrStream;
	wstrStream << queryResult;
	std::wstring result = wstrStream.str();

	wchar_t *buffer = new wchar_t[result.length() + 1];
	memcpy(buffer, result.c_str(), result.length() + 1);

	FreeQuery();

	return buffer;
}

wchar_t *SQLiteStatement::GetSqlResultString16(const std::string &sql, wchar_t *defaultReturnValue) 
{
	Sql(sql);
	return SqlResultString16(defaultReturnValue);
}

wchar_t *SQLiteStatement::GetSqlResultString16(const char *sql, wchar_t *defaultReturnValue) 
{
	Sql(sql);
	return SqlResultString16(defaultReturnValue);
}

wchar_t *SQLiteStatement::GetSqlResultString16(const wchar_t *sql, wchar_t *defaultReturnValue) 
{
	Sql(sql);
	return SqlResultString16(defaultReturnValue);
}

const void *SQLiteStatement::SqlResultBlob(const void *defaultReturnValue)
{
	const void *queryResult;

	if(!FetchRow())
		queryResult = defaultReturnValue;
	else
		queryResult = SQLiteStatement::GetColumnBlob(0);

	std::stringstream strStream;
	strStream << static_cast<const char *>(queryResult);
	std::string result = strStream.str();

	char *buffer = new char[result.length() + 1];
	memcpy(buffer, result.c_str(), result.length() + 1);

	FreeQuery();

	return static_cast<void *>(buffer);
}

const void *SQLiteStatement::GetSqlResultBlob(const std::string &sql, const void *defaultReturnValue) 
{
	Sql(sql);
	return SqlResultBlob(defaultReturnValue);
}

const void *SQLiteStatement::GetSqlResultBlob(const char *sql, const void *defaultReturnValue) 
{
	Sql(sql);
	return SqlResultBlob(defaultReturnValue);
}

const void *SQLiteStatement::GetSqlResultBlob(const wchar_t *sql, const void *defaultReturnValue) 
{
	Sql(sql);
	return SqlResultBlob(defaultReturnValue);
}

//------------------------------------------------------------------------------------
unsigned int SQLiteStatement::GetNumberOfRows()
{
	unsigned int count = 0;
	while(FetchRow())
		++count;

	Reset();
	return count;
}

void SQLiteStatement::CheckColumnNumber(int columnNumber, const std::string &functionName) const
{
    if(columnNumber < 0 || columnNumber >= sqlite3_column_count(mStatement))
        KOMPEX_EXCEPT(functionName + " column number does not exists", -1);
}

//------------------------------------------------------------------------------------
void SQLiteStatement::AssignColumnNumberToColumnName() const
{
	CheckStatement();
	
	// a previous executed SELECT query is necessary
	if(!mIsColumnNumberAssignedToColumnName && sqlite3_column_count(mStatement) >= 0)
	{
		// delete old entries
		mColumnNumberToColumnNameAssignment.erase(mColumnNumberToColumnNameAssignment.begin(), mColumnNumberToColumnNameAssignment.end());
		
		for(int i = 0; i < sqlite3_column_count(mStatement); ++i)
			mColumnNumberToColumnNameAssignment[sqlite3_column_name(mStatement, i)] = i;

		mIsColumnNumberAssignedToColumnName = true;
    }
}

int SQLiteStatement::GetAssignedColumnNumber(const std::string &columnName) const
{
	std::map<std::string, int>::const_iterator iter = mColumnNumberToColumnNameAssignment.find(columnName);

	if(iter == mColumnNumberToColumnNameAssignment.end())
	{
		// if you don't catch the exception then we will return -1 so that the function sqlite3_column_*()
		// will return a undefined value
		KOMPEX_EXCEPT("GetAssignedColumnNumber() column name '" + columnName + "' does not exists", -1);
		return -1;
	}

	return iter->second;
}

std::string SQLiteStatement::Mprintf(const char *sql, ...)
{
    va_list args;
    va_start(args, sql);
	char *sqlResult = sqlite3_vmprintf(sql, args);
	va_end(args);

	if(!sqlResult)
	{
		KOMPEX_EXCEPT("unable to allocate enough memory to hold the resulting string", -1);
		return "";
	}

	std::string result = sqlResult;
	sqlite3_free(sqlResult);
	return result;
}

std::string SQLiteStatement::Vmprintf(const char *sql, va_list args)
{
	char *sqlResult = sqlite3_vmprintf(sql, args);

	if(!sqlResult)
	{
		KOMPEX_EXCEPT("unable to allocate enough memory to hold the resulting string", -1);
		return "";
	}

	std::string result = sqlResult;
	sqlite3_free(sqlResult);
	return result;
}

}	// namespace Kompex
